# BlackSEO Detector Proof of Concept

=begin
Copyright (C) 2014 Enrique Rando

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.

http://www.gnu.org/licenses/gpl-2.0.html
=end




require File.expand_path(File.dirname(__FILE__) + '/BlackSEO_detector.rb')
require File.expand_path(File.dirname(__FILE__) + '/Report_generator.rb')
require File.expand_path(File.dirname(__FILE__) + '/My_configuration.rb')
require File.expand_path(File.dirname(__FILE__) + '/My_mailer.rb')


# Creates Search Engine Objects from the config information
# Parameter 	config	the app config information
def create_search_engines(config)
	# Get Search engine definitions
	s_e_data = {}
	config[:search_engine].each do |p|
		p.each do |name, cl|
			s_e_data[name] = {
				:class => cl,
				:file => cl+".rb",
				:options => []
			}
		end
	end
	
	# Get object constructor parameters
	config[:search_engine_option].each do |p|
		p.each do |s_e, option|
			raise "Invalid Search Engine Name: #{s_e}" if not s_e_data.has_key?(s_e)
			s_e_data[s_e][:options] << option
		end
	end
	
	
	# Create the Search Engines
	search_engines = {}
	s_e_data.each do |name, data|
	# Load the class file and gets the class
		require File.expand_path(File.dirname(__FILE__) + '/searchengines/' + data[:file])
		myclass = eval(data[:class])

		# Create a Search Engine
		search_engine = myclass.new(*data[:options])
		search_engine.connect_through(config[:proxy], config[:proxy_port], config[:proxy_user], config[:proxy_password]) unless config[:proxy].nil?

		# Add to Search Engine List
		search_engines[name] = search_engine
	end

	return search_engines
end


# Read project file
# Parameter	file	project config file path
def read_config(file)
	# Define options
	option_definitions = [
	{:name=>"verbose", :type=>:boolean, :default=>"N"},
	{:name=>"language", :default=>"spanish"},
	{:name=>"search_engine", :kind=>:pairs, :mandatory=>"Y"},
	{:name=>"search_engine_option", :kind=>:pairs},
	{:name=>"title", :default=>"Report"},
	{:name=>"report_file", :mandatory=>"Y"}, {:name=>"add_date_time", :type=>:boolean, :default=>"Y"}, 
	{:name=>"overwrite_report", :type=>:boolean, :default=>"N"},
	{:name=>"proxy", :allowed=>:ENV, :default=>:ENV}, {:name=>"proxy_port"}, {:name=>"proxy_user"}, {:name=>"proxy_password"},
	{:name=>"user_agent", :default=>"BlackSeo Detector"},
	{:name=>"max_redirects", :type=>:integer, :default=>5},
	{:name=>"hard_check", :type=>:boolean, :default=>"N"},
	{:name=>"max_report", :type=>:integer, :default=>25}, {:name=>"max_host", :type=>:integer, :default=>15}, {:name=>"max_folder", :type=>:integer, :default=>5},
	{:name=>"send_mail", :type=>:boolean, :default=>"N"}, {:name=>"mail_server"}, {:name=>"mail_port", :type=>:integer, :allowed=>:default, :default=>:default}, 
	{:name=>"mail_cypher", :type=>:symbol, :default=>:tls},
	{:name=>"mail_user"}, {:name=>"mail_password"}, {:name=>"mail_auth", :type=>:symbol, :default=>:login},
	{:name=>"mail_from"}, {:name=>"mail_to"}, {:name=>"mail_subject", :default=>"BlackSeo Detector Results"},
	{:name=>"mail_body", :kind=>:multiple},
	{:name=>"text", :kind=>:multiple},
	{:name=>"allowed_domain", :kind=>:multiple},
	{:name=>"ignore_files", :kind=>:multiple, :type=>:regexp},
	{:name=>"query", :kind=>:pairs, :mandatory=>"Y"}
	]

	# Read the config
	config = My_configuration.new(option_definitions)
	config.read(file)
	
	return config
end


# Check arguments
count = ARGV.count
if count < 1
print "Usage:
  
  Launching a project
      #{File.basename($0)} config_file
"
      exit
end


# Project file
project_file = File.expand_path(ARGV[0])

# Process project file
begin
	raise  "File '#{project_file}' doesn't exist" if not File.exist?(project_file)
	config = read_config(project_file)
	config[:report_file] = config[:report_file] + Time.new.strftime("%Y%m%d-%H%M%S%L") + ".html" if config[:add_date_time]
	config[:report_file] = File.expand_path(config[:report_file], File.dirname(project_file))
	
	params = []
	params << create_search_engines(config)
	params << config[:user_agent] 
	params << config[:proxy] 
	params << config[:proxy_port] 
	params << config[:proxy_user] 
	params << config[:proxy_password] 
	params << config[:verbose]
rescue Exception => e
	print "\nError loading config file: #{e.message}\n\n"
	print e.backtrace.join("\n");
	exit
end

# Set up environment
begin
	lang_file = File.expand_path(File.dirname(__FILE__) + '/lang/' + config[:language] + ".txt")
	raise  "Language file '#{lang_file}' doesn't exist" unless File.exist?(lang_file)
	bsd = BlackSEO_detector.new(*params)
	rep = Report_generator.new(config[:report_file], config[:overwrite_report], lang_file)
rescue Exception => e
	print "\nError while seting up environment: #{e.message}\n\n"
	print e.backtrace.join("\n");
	exit
end


# Make the hard work
begin
	rep.begin(config[:title])
	problems_found = bsd.investigate(config[:query], config[:text], rep, '', config[:hard_check], 
			config[:max_redirects], config[:allowed_domain], config[:ignore_files],
			config[:max_report], config[:max_host], config[:max_folder])
	rep.end
rescue Exception => e
	print "\nError while investigating: #{e.message}\n\n"
	print e.backtrace.join("\n");
	exit
end

# Send E-mail
begin
	if config[:send_mail]
		attachments = [
			{
				:file_name => config[:report_file],
				:content_type => 'text/html'
			}
		]

		m = My_mail_sender.new(config[:mail_server], config[:mail_port], config[:mail_cypher], 
									config[:mail_user], config[:mail_password], config[:mail_auth])
									
		mail_body = config[:mail_body].empty? ?
				["Your BlackSEO detector report is ready. Number of problems detected: [[problems_found]].", 
				"",
				"Main report file is attached to this message",
				"",
				"See on site full report for details. Its path is [[report_file]]"]
			:
				config[:mail_body]
				
		mail_body = mail_body.collect{|line| line=="." ? "" : line.gsub("[[problems_found]]", problems_found.to_s).gsub("[[report_file]]", config[:report_file])}.join("\n")
				
		m.send(mail_body, 
			config[:mail_from], config[:mail_to], config[:mail_subject],
			"text/plain",attachments)
	end
rescue Exception => e
	print "\nError while sending e-mail: #{e.message}\n\n"
	print e.backtrace.join("\n");
	exit
end